#ifndef INC_ENTITY_H_
#define INC_ENTITY_H_

#include <cassert>
#include <d3dx9.h>

//-----------------------------------------------------------------------------



struct TEntity3D {

	D3DXMATRIX  world;
	//LPDIRECT3DVERTEXBUFFER9 v_buffer;    // the pointer to the vertex buffer
	//LPDIRECT3DINDEXBUFFER9 i_buffer;    // the pointer to the index buffer
	LPD3DXMESH          g_pMesh;// Our mesh object in sysmem
	D3DMATERIAL9*       g_pMeshMaterials; // Materials for our mesh
LPDIRECT3DTEXTURE9* g_pMeshTextures; // Textures for our mesh
DWORD               g_dwNumMaterials;   // Number of mesh materials

	void setPosition( const D3DXVECTOR3 &new_pos ) {
		world.m[3][0] = new_pos.x;
		world.m[3][1] = new_pos.y;
		world.m[3][2] = new_pos.z;
	}


	/*VOID Cleanup()
{
    if( g_pMeshMaterials != NULL )
        delete[] g_pMeshMaterials;

    if( g_pMeshTextures )
    {
        for( DWORD i = 0; i < g_dwNumMaterials; i++ )
        {
            if( g_pMeshTextures[i] )
                g_pMeshTextures[i]->Release();
        }
        delete[] g_pMeshTextures;
    }
    if( g_pMesh != NULL )
        g_pMesh->Release();

    if( g_pd3dDevice != NULL )
        g_pd3dDevice->Release();

    if( g_pD3D != NULL )
        g_pD3D->Release();
}*/


	// this is the function that puts the 3D models into video RAM
HRESULT init_graphics(LPDIRECT3DDEVICE9  g_pd3dDevice)
{
    // create the vertices using the CUSTOMVERTEX
	//v_buffer = NULL;
	//i_buffer = NULL;
	g_pMesh = NULL;
	g_pMeshMaterials = NULL;
	g_pMeshMaterials = NULL;
	g_pMeshTextures = NULL; 
	g_dwNumMaterials = 0L; 
	LPD3DXBUFFER pD3DXMtrlBuffer;
    /*struct CUSTOMVERTEX vertices[] =
    {
        // fuselage3.0f, 0.0f, 0.0f, 
		{ D3DXVECTOR3(3.0f, 0.0f, 0.0f), D3DCOLOR_XRGB(0, 255, 0), },
        {  D3DXVECTOR3(0.0f, 3.0f, -3.0f), D3DCOLOR_XRGB(0, 0, 255), },
        {  D3DXVECTOR3(0.0f, 0.0f, 10.0f), D3DCOLOR_XRGB(255, 0, 0), },
        {  D3DXVECTOR3(-3.0f, 0.0f, 0.0f), D3DCOLOR_XRGB(0, 255, 255), },

        // left gun
        {  D3DXVECTOR3(3.2f, -1.0f, -3.0f), D3DCOLOR_XRGB(0, 0, 255), },
        {  D3DXVECTOR3(3.2f, -1.0f, 11.0f), D3DCOLOR_XRGB(0, 255, 0), },
        {  D3DXVECTOR3(2.0f, 1.0f, 2.0f), D3DCOLOR_XRGB(255, 0, 0), },

        // right gun
        {  D3DXVECTOR3(-3.2f, -1.0f, -3.0f), D3DCOLOR_XRGB(0, 0, 255), },
        {  D3DXVECTOR3(-3.2f, -1.0f, 11.0f), D3DCOLOR_XRGB(0, 255, 0), },
        {  D3DXVECTOR3(-2.0f, 1.0f, 2.0f), D3DCOLOR_XRGB(255, 0, 0), },
    };*/

    // create a vertex buffer interface called v_buffer
   /* g_pd3dDevice->CreateVertexBuffer(10*sizeof(CUSTOMVERTEX),
                               0,
                               D3DFVF_CUSTOMVERTEX,
                               D3DPOOL_MANAGED,
                               &v_buffer,
                               NULL);
*/
   // VOID* pVoid;    // a void pointer
	if( FAILED( D3DXLoadMeshFromX( "car.x", D3DXMESH_SYSTEMMEM,
                                   g_pd3dDevice, NULL,
                                   &pD3DXMtrlBuffer, NULL, &g_dwNumMaterials,
                                   &g_pMesh ) ) )
    {
        // If model is not in current folder, try parent folder
        if( FAILED( D3DXLoadMeshFromX( "..\\car.x", D3DXMESH_SYSTEMMEM,
                                       g_pd3dDevice, NULL,
                                       &pD3DXMtrlBuffer, NULL, &g_dwNumMaterials,
                                       &g_pMesh ) ) )
        {
            MessageBox( NULL, "Could not find car.x", "Meshes.exe", MB_OK );
            return E_FAIL;
        }
    }

	// We need to extract the material properties and texture names from the 
    // pD3DXMtrlBuffer
    D3DXMATERIAL* d3dxMaterials = ( D3DXMATERIAL* )pD3DXMtrlBuffer->GetBufferPointer();
    g_pMeshMaterials = new D3DMATERIAL9[g_dwNumMaterials];
    if( g_pMeshMaterials == NULL )
        return E_OUTOFMEMORY;
    g_pMeshTextures = new LPDIRECT3DTEXTURE9[g_dwNumMaterials];
    if( g_pMeshTextures == NULL )
        return E_OUTOFMEMORY;
/*

    // lock v_buffer and load the vertices into it
    v_buffer->Lock(0, 0, (void**)&pVoid, 0);
    memcpy(pVoid, vertices, sizeof(vertices));
    v_buffer->Unlock();

    // create the indices using an int array
    short indices[] =
    {
        0, 1, 2,    // fuselage
        2, 1, 3,
        3, 1, 0,
        0, 2, 3,
        4, 5, 6,    // wings
        7, 8, 9,
    };

    // create a index buffer interface called i_buffer
    g_pd3dDevice->CreateIndexBuffer(18*sizeof(short),
                              0,
                              D3DFMT_INDEX16,
                              D3DPOOL_MANAGED,
                              &i_buffer,
                              NULL);

    // lock i_buffer and load the indices into it
    i_buffer->Lock(0, 0, (void**)&pVoid, 0);
    memcpy(pVoid, indices, sizeof(indices));
    i_buffer->Unlock(); */

	for( DWORD i = 0; i < g_dwNumMaterials; i++ )
    {
        // Copy the material
        g_pMeshMaterials[i] = d3dxMaterials[i].MatD3D;

        // Set the ambient color for the material (D3DX does not do this)
        g_pMeshMaterials[i].Ambient = g_pMeshMaterials[i].Diffuse;

        g_pMeshTextures[i] = NULL;
        if( d3dxMaterials[i].pTextureFilename != NULL &&
            lstrlenA( d3dxMaterials[i].pTextureFilename ) > 0 )
        {
            // Create the texture
            if( FAILED( D3DXCreateTextureFromFileA( g_pd3dDevice,
                                                    d3dxMaterials[i].pTextureFilename,
                                                    &g_pMeshTextures[i] ) ) )
            {
                // If texture is not in current folder, try parent folder
                const CHAR* strPrefix = "..\\";
                CHAR strTexture[MAX_PATH];
                strcpy_s( strTexture, MAX_PATH, strPrefix );
                strcat_s( strTexture, MAX_PATH, d3dxMaterials[i].pTextureFilename );
                // If texture is not in current folder, try parent folder
                if( FAILED( D3DXCreateTextureFromFileA( g_pd3dDevice,
                                                        strTexture,
                                                        &g_pMeshTextures[i] ) ) )
                {
                    MessageBox( NULL, "Could not find texture map", "Meshes.exe", MB_OK );
                }
            }
        }
    }

    // Done with the material buffer
    pD3DXMtrlBuffer->Release();


}

	void move( const D3DXVECTOR3 &delta ) {
		world.m[3][0] += delta.x;
		world.m[3][1] += delta.y;
		world.m[3][2] += delta.z;
	}

	void rotate( const D3DXVECTOR3& axis, float radians ) {
		D3DXMATRIX delta;
		D3DXMatrixRotationAxis( &delta, &axis, radians );
		// p' = plocal * world * view * proj
		world = delta * world;
	}

	void rotateY( float radians ) {
		rotate( D3DXVECTOR3(0,1,0), radians );
	}

	D3DXVECTOR3 getPosition() {
		return D3DXVECTOR3( world.m[3][0]
		                  , world.m[3][1]
		                  , world.m[3][2]
						  );
	}

	D3DXVECTOR3 getFront( ) {
		D3DXVECTOR3 f( world.m[2][0]
		             , world.m[2][1]
		             , world.m[2][2]
					 );
		 // Si esto falla es que hemos escalado
		 // la matriz de world y tendriamoss que
		 // normalizar f siempre
		//assert( fabsf( D3DXVec3Length( &f ) - 1.0f ) < 1e-6 );
		return f;
	}

	D3DXVECTOR3 getLeft( ) {
		return D3DXVECTOR3( world.m[0][0]
		                  , world.m[0][1]
		                  , world.m[0][2]
						  );
	}

	D3DXVECTOR3 getUp( ) {
		return D3DXVECTOR3( world.m[1][0]
		                  , world.m[1][1]
		                  , world.m[1][2]
						  );
	}

	// ---------------------------
	TEntity3D( ) {
		D3DXMatrixIdentity( &world );
	}

	virtual ~TEntity3D( ) { }

	virtual void render(LPDIRECT3DDEVICE9  g_pd3dDevice ) { 
				D3DXMATRIX matScale;    // a matrix to store the scaling information
				
	// build a matrix to double the size of the model
	// store it to matScale
	D3DXMatrixScaling(&matScale, 0.2f, 0.2f, 0.2f);
	

	// tell Direct3D about our matrix
		g_pd3dDevice->SetTransform( D3DTS_WORLD, &(world * matScale) );
		// g_pd3dDevice->SetStreamSource(0, v_buffer, 0, sizeof(CUSTOMVERTEX));
		//g_pd3dDevice->SetIndices(i_buffer);
		 //g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 10, 0, 6);
		//drawAxis();
	
        // Meshes are divided into subsets, one for each material. Render them in
        // a loop
        for( DWORD i = 0; i < g_dwNumMaterials; i++ )
        {
            // Set the material and texture for this subset
            g_pd3dDevice->SetMaterial( &g_pMeshMaterials[i] );
            g_pd3dDevice->SetTexture( 0, g_pMeshTextures[i] );

            // Draw the mesh subset
            g_pMesh->DrawSubset( i );
        }
    }
};

#endif
